#include "connection.h"
#include "server.h"

connection::connection(int fd)
:fd_(fd)
,querybuf_pos_(0)
,buf_pos_(0)
,sendlen_(0)
,flags_(0)
{
    memset(query_buf_, 0, sizeof(query_buf_));
    memset(buf_, 0, sizeof(buf_));
}

connection::~connection()
    {
        std::list<replyBlock*>::iterator it = reply_.begin();
        for(; it != reply_.end(); ++it)
        {
            if(*it)
            {
                free(*it);
            }
        }
        reply_.clear();

        if(endpoint_)
        {
            tcfree(HttpEndPoint, endpoint_);
            endpoint_ = nullptr;
        }

        if(fd_ > 0)
        {
            close(fd_);
            fd_ = -1;
        }
    }

void connection::reset()
{
    fd_ = querybuf_pos_= buf_pos_= sendlen_ = flags_= 0;
    memset(query_buf_, 0, sizeof(query_buf_));
    memset(buf_, 0, sizeof(buf_));
}

int connection::Socket()
{
    return socket(AF_INET, SOCK_STREAM, 0);
}

int connection::Bind(int fd, int port)
{
  struct sockaddr_in addr;
  addr.sin_family = AF_INET;
  addr.sin_port = htons(port);
  addr.sin_addr.s_addr = htonl(INADDR_ANY) ;

  return bind(fd, (struct sockaddr*)&addr , sizeof(addr));
}

int connection::Listen(int fd)
{
  return listen(fd, LISTEN_NUM);
}

int connection::Accept(int fd, struct sockaddr_in* addr, socklen_t* len)
{
  return accept(fd, (sockaddr*)addr, len);
}

int connection::SetSockOpt(int fd)
{
  int opt = 1;
  return setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
}

int connection::setNoBlock()
{
  int fl = fcntl(fd_, F_GETFL);
  if(fl < 0)
  {
    return -1;
  }

  if(fcntl(fd_, F_SETFL, fl | O_NONBLOCK) < 0)
  {
    return -1;
  }
  return 0;
}

bool connection::clientHasPendingReplies()
{
    return buf_pos_ != 0;
}

void connection::processInputBuffer()
{
    int len = strlen(query_buf_);
    endpoint_->parseRequest(query_buf_, len);
    endpoint_->makeResponse();
    endpoint_->sendResponse();
    // while(querybuf_pos_ < len)
    // {
    //     buf_[buf_pos_++] = query_buf_[querybuf_pos_++];
    // }

    // buf_[buf_pos_++] = ':';
    // std::string str =  std::to_string(pthread_self());
    // strcpy(&buf_[buf_pos_], str.c_str());
    // buf_pos_ += str.size();

    //addReply(query_buf_, len);

    query_buf_[0] = '\0';
    querybuf_pos_ = 0;
}

bool connection::hasPendingReplies()
{
    return buf_pos_ > 0 || reply_.size() > 0;
}

void connection::addReply(const char* msg, size_t len)
{
    if(!addReplyToBuff(msg, len))
    {
        addReplyToList(msg, len);
    }
}

bool connection::addReplyToBuff(const char* msg, size_t len)
{
    if(reply_.size() > 0)
        return false;  

    size_t avaliable = sizeof(buf_) - buf_pos_;

    //size_t len = strlen(msg);

    if(avaliable < len)
        return false;
    
    memcpy(buf_ + buf_pos_, msg, len);
    buf_pos_ += len;
    return true;
}

bool connection::addReplyToList(const char* msg, size_t len)
{
    //int len = strlen(msg);
    replyBlock* back = reply_.empty() ? nullptr : reply_.back();

    //back->size_ - back->used_  >= len 这是因为测试时jpg数据分开发送，导致接收端数据与服务器的不一致
    //所以剩余空间不足时，新建一个reply
    if(back &&  back->size_ - back->used_  >= len)
    {
        size_t avail = back->size_ - back->used_;
        size_t copy = len >= avail ? avail : len;
        memcpy(&back->buf_[back->used_], msg, copy);
        //std::cout << back->buf_ << std::endl;
        back->used_ += copy;
        len -= copy;
    }

    if(len)
    {
        //新建reply节点
        size_t size = len > PROTO_REPLY_CHUNK_BYTES ? len : PROTO_REPLY_CHUNK_BYTES;
        //size_t size = len;
        //没有调构造函数
        //void* ptr= malloc(sizeof(replyBlock) + size * sizeof(char));
        //new_reply->used_ = 0;
        //new_reply->size_ = 0;
        //void* ptr = operator new(sizeof(replyBlock) + size * sizeof(char));
        //size_t total_size = sizeof(replyBlock) + size * sizeof(char);
        //调用构造函数
        // size_t total_size = sizeof(replyBlock);
        // void* ptr = cAlloc(total_size);
        // replyBlock* new_reply = (replyBlock*)(new (ptr) replyBlock(size));
        replyBlock* new_reply  = tcallocSize(replyBlock, size, size);

        assert(new_reply != nullptr);
        new_reply->size_ = size;
        assert(len <= size);
        memcpy(new_reply->buf_, msg, len);
        new_reply->used_ += len;
        reply_.push_back(new_reply);
        reply_size_ += new_reply->size_;
    }
}

int connection::connnSocketSetWriteHandler(ConnectionCallbackFunc func)
{
    if(func == write_func_) return 0;

    write_func_ = func;
    if(write_func_ == nullptr)
    {
        server.el_->deleteFileEvent(fd_, AE_WRITABLE);
    }
    else
    {
        if(server.el_->createFileEvent(fd_, AE_WRITABLE, eventLoop::connSocketEventHandler, this) < 0)
            return -1;
    }
    return 0;
}